/*
 * Rabobong v.1.20.0 es un equipo de futbol que pretende participar del JavaCup 2011.
    Copyright (C) 2011  freymam.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */

package org.javahispano.javacup.tacticas.tacticas_aceptadas.rabobong;

import java.awt.Color;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.javahispano.javacup.modelo.comando.Comando;
import org.javahispano.javacup.modelo.comando.ComandoGolpearBalon;
import org.javahispano.javacup.modelo.comando.ComandoIrA;
import org.javahispano.javacup.modelo.util.Constantes;
import org.javahispano.javacup.render.EstiloUniforme;
import org.javahispano.javacup.modelo.JugadorDetalle;
import org.javahispano.javacup.modelo.util.Posicion;
import org.javahispano.javacup.modelo.engine.SituacionPartido;
import org.javahispano.javacup.modelo.Tactica;
import org.javahispano.javacup.modelo.TacticaDetalle;

public class Rabobong implements Tactica {

	Posicion alineacion1[]=new Posicion[]{
	        new Posicion(0.0,-48.79864253393666),
	        new Posicion(-17.11888111888112,-27.31900452488688),
	        new Posicion(0.7132867132867133,-31.357466063348415),
	        new Posicion(15.692307692307693,-27.794117647058822),
	        new Posicion(-10.223776223776223,-19.479638009049776),
	        new Posicion(8.797202797202797,-14.728506787330318),
	        new Posicion(-1.188811188811189,-11.402714932126697),
	        new Posicion(-7.608391608391608,-4.276018099547511),
	        new Posicion(0.7132867132867133,0.0),
	        new Posicion(-22.11188811188811,-0.47511312217194573),
	        new Posicion(19.97202797202797,-0.47511312217194573)
	    };

	    Posicion alineacion2[]=new Posicion[]{
	        new Posicion(0.0,-48.79864253393666),
	        new Posicion(-17.11888111888112,-27.31900452488688),
	        new Posicion(-0.4755244755244755,-30.88235294117647),
	        new Posicion(15.692307692307693,-27.794117647058822),
	        new Posicion(-10.223776223776223,-19.479638009049776),
	        new Posicion(6.895104895104895,-19.95475113122172),
	        new Posicion(-1.6643356643356644,-16.628959276018097),
	        new Posicion(-5.468531468531468,-7.364253393665159),
	        new Posicion(5.468531468531468,-7.364253393665159),
	        new Posicion(-22.11188811188811,-0.47511312217194573),
	        new Posicion(19.97202797202797,-0.47511312217194573)
	    };

	    Posicion alineacion3[]=new Posicion[]{
	        new Posicion(0.0,-43.43438914027149),
	        new Posicion(-16.405594405594407,-14.015837104072398),
	        new Posicion(-0.23776223776223776,-16.866515837104075),
	        new Posicion(15.692307692307693,-13.778280542986426),
	        new Posicion(-10.937062937062937,10.690045248868778),
	        new Posicion(7.37062937062937,2.6131221719457014),
	        new Posicion(21.874125874125873,18.29185520361991),
	        new Posicion(-24.251748251748253,26.368778280542987),
	        new Posicion(1.902097902097902,27.556561085972852),
	        new Posicion(-11.174825174825173,38.72171945701358),
	        new Posicion(14.503496503496503,40.38461538461539)
	    };

	    Posicion alineacion4[]=new Posicion[]{
	        new Posicion(0.0,-50.93665158371041),
	        new Posicion(-16.405594405594407,-31.119909502262445),
	        new Posicion(-0.4755244755244755,-34.20814479638009),
	        new Posicion(16.405594405594407,-32.07013574660634),
	        new Posicion(-9.748251748251748,-12.59049773755656),
	        new Posicion(7.37062937062937,-17.57918552036199),
	        new Posicion(15.454545454545453,1.4253393665158371),
	        new Posicion(-19.734265734265733,4.038461538461538),
	        new Posicion(-2.13986013986014,12.115384615384617),
	        new Posicion(-10.937062937062937,25.893665158371043),
	        new Posicion(8.083916083916083,26.131221719457013)
	    };

	    Posicion alineacion5[]=new Posicion[]{
	        new Posicion(0.0,-48.660633484162894),
	        new Posicion(-16.405594405594407,-22.330316742081447),
	        new Posicion(-0.23776223776223776,-26.131221719457013),
	        new Posicion(15.93006993006993,-23.042986425339365),
	        new Posicion(-10.223776223776223,0.47511312217194573),
	        new Posicion(6.6573426573426575,-7.126696832579185),
	        new Posicion(17.832167832167833,13.540723981900454),
	        new Posicion(-19.25874125874126,16.866515837104075),
	        new Posicion(-1.188811188811189,21.380090497737555),
	        new Posicion(-11.65034965034965,36.58371040723982),
	        new Posicion(12.363636363636363,36.82126696832579)
	    };

	    Posicion alineacion6[]=new Posicion[]{
	        new Posicion(0.,-42.62217194570136),
	        new Posicion(-16.405594405594407,-22.330316742081447),
	        new Posicion(-0.23776223776223776,-26.131221719457013),
	        new Posicion(15.93006993006993,-23.042986425339365),
	        new Posicion(-9.986013986013985,-3.5633484162895925),
	        new Posicion(6.895104895104895,-6.889140271493213),
	        new Posicion(15.93006993006993,12.828054298642533),
	        new Posicion(-19.020979020979023,11.877828054298643),
	        new Posicion(-0.951048951048951,18.766968325791854),
	        new Posicion(-11.65034965034965,36.58371040723982),
	        new Posicion(12.363636363636363,36.82126696832579)
	    };

    class TacticaDetalleImpl implements TacticaDetalle {

        public String getNombre() {
            return "Rabobong";
        }

        public String getPais() {
            return "Colombia";
        }

        public String getEntrenador() {
            return "Freymam Vallejo";
        }

        public Color getColorCamiseta() {
            return new Color(255, 153, 0);
        }

        public Color getColorPantalon() {
            return new Color(0, 0, 102);
        }

        public Color getColorFranja() {
            return new Color(255, 255, 255);
        }

        public Color getColorCalcetas() {
            return new Color(255, 153, 0);
        }

        public Color getColorPortero() {
            return new Color(0, 153, 0        );
        }

        public EstiloUniforme getEstilo() {
            return EstiloUniforme.SIN_ESTILO;
        }

        public Color getColorCamiseta2() {
            return new Color(0, 0, 102);
        }

        public Color getColorPantalon2() {
            return new Color(0, 0, 102);
        }

        public Color getColorFranja2() {
            return new Color(255, 255, 255);
        }

        public Color getColorCalcetas2() {
            return new Color(255, 153, 0);
        }

        public Color getColorPortero2() {
            return new Color(255, 0, 0        );
        }

        public EstiloUniforme getEstilo2() {
            return EstiloUniforme.SIN_ESTILO;
        }

        class JugadorImpl implements JugadorDetalle {

            String nombre;
            int numero;
            Color piel, pelo;
            double velocidad, remate, presicion;
            boolean portero;
            Posicion posicion;

            public JugadorImpl(String nombre, int numero, Color piel, Color pelo,
                    double velocidad, double remate, double presicion, boolean portero) {
                this.nombre=nombre;
                this.numero=numero;
                this.piel=piel;
                this.pelo=pelo;
                this.velocidad=velocidad;
                this.remate=remate;
                this.presicion=presicion;
                this.portero=portero;
            }

            public String getNombre() {
                return nombre;
            }

            public Color getColorPiel() {
                return piel;
            }

            public Color getColorPelo() {
                return pelo;
            }

            public int getNumero() {
                return numero;
            }

            public boolean esPortero() {
                return portero;
            }

            public double getVelocidad() {
                return velocidad;
            }

            public double getRemate() {
                return remate;
            }

            public double getPresicion() {
                return presicion;
            }

        }

        public JugadorDetalle[] getJugadores() {
            return new JugadorDetalle[]{
                new JugadorImpl("Oscor Feyde", 20, new Color(102,51,0), new Color(50,0,0),1.0d,1.0d,0.58d, true),
                new JugadorImpl("Haymen", 21, new Color(204,153,0), new Color(50,0,0),1.0d,0.69d,0.69d, false),
                new JugadorImpl("Walkar", 22, new Color(153,102,0), new Color(50,0,0),0.81d,0.9d,0.81d, false),
                new JugadorImpl("Mann Dimar", 23, new Color(102,0,0), new Color(50,0,0),1.0d,0.74d,0.76d, false),
                new JugadorImpl("Van Heeswick", 24, new Color(255,200,150), new Color(50,0,0),0.91d,0.47d,0.95d, false),
                new JugadorImpl("Juust Posthunna", 25, new Color(255,204,204), new Color(50,0,0),0.61d,0.86d,0.85d, false),
                new JugadorImpl("Johan Fecha", 26, new Color(255,204,102), new Color(50,0,0),0.57d,0.49d,1.0d, false),
                new JugadorImpl("Theodoro Eltin", 27, new Color(102,102,0), new Color(50,0,0),0.58d,0.54d,1.0d, false),
                new JugadorImpl("Thomas Dakkor", 28, new Color(102,0,51), new Color(50,0,0),1.0d,1.0d,1.0d, false),
                new JugadorImpl("Robert Geskin", 29, new Color(51,0,0), new Color(50,0,0),0.9d,0.9d,0.89d, false),
                new JugadorImpl("Donis Henchov", 30, new Color(179,145,114), new Color(50,0,0),1.0d,1.0d,1.0d, false)
            };
        }
    }

    TacticaDetalle detalle=new TacticaDetalleImpl();
    public TacticaDetalle getDetalle() {
        return detalle;
    }

    public Posicion[] getPosicionSaca(SituacionPartido sp) {
    return alineacion1;
    }

    public Posicion[] getPosicionRecive(SituacionPartido sp) {
    return alineacion2;
    }

	public Posicion[] getPosicionAtaque() {
		return alineacion3;
	}

	public Posicion[] getPosicionDefensa() {
		return alineacion4;
	}

	public Posicion[] getPosicionMedia() {
		return alineacion5;
	}

	public Posicion[] getPosicionEstirado() {
		return alineacion6;
	}

	// OBJETOS GLOBALES
	Random r = new Random();
	Posicion[] formacionActual;
	// las direcciones hacia donde se desmarcaran
	double[] desmarcandoseDireccion = new double[11];
	// iteraciones faltantes para dejar ese desmarcaje y buscar otro
	int[] desmarcandoseIteracion = new int[11];
	//Posicion referenciaPortero = Constantes.centroArcoInf.moverPosicion(0, -15);
	Posicion referenciaPortero = Constantes.centroArcoInf;
	LinkedList<Comando> comandos = new LinkedList<Comando>();

	// MIS VARIABLES GLOBALES
	double dispararAGol = 0;
	double darPase = 0;
	double acarrearBalon = 0;
	int jugadorDestino = -1;
	int jugadorOrigen = -1;
	boolean posecionBalon = false;
	boolean posec = false;
	boolean jugando = true;
	double anguloEspacio;
	double distanciaEspacio;
	Posicion posicionDestino;
	double velocidadBalon = 0;
	int indPorteroRival = -1;
	Posicion porteroRival;
	int indPortero = 0;
	int desespero = 0;

	// MIS CONSTANTES
	// Tengo el balon
	double DISPARO_MORTAL = 77.0; //78.0;
	double PASE_MORTAL = 81.0;
	double ACARREO_MORTAL = 85.0;
	double MINIMO_DISPARA_A_GOL = 65.0;
	double DARPASE = 63.5; //66.0;
	double MINIMO_ACARREAR_BALON = 65.0;// 67.3;
	// AnguloVerticalPase
	int MAX_DISTANCIA_PASE_RASTRERO = 22;
	int MAX_DISTANCIA_PASE_AEREO = 50;
	// Cubrir pase
	int DISTANCIA_MAXIMA_BUSQUEDA_RIVAL = 30;
	int DISTANCIA_MARCAJE_RIVAL = 5;
	int JUGADORES_MARCANDO = 4;
	int DISTANCIA_CORRECCION_FORMACION_MARCAJE = 15;
	// Pase exitoso
	int MIN_DISTANCIA_PASE = 7;
	double probDistanciaPase = 0.13;
	double probPresenciaRival = 0.16;
	double probHaciaAdelante = 0.21;
	double probCercaniaAlArcoRival = 0.26;
	double probLlegarPrimero = 0.24;
	// Hacia adelante
	int AVANCE_MEDIA_PASE_RETRASADO = 5;
	int AVANCE_MAXIMA_PASE_RETRASADO = 20;
	int AVANCE_MAXIMA_PASE_ADELANTE = 35;
	// Presencia rival
	double DISTANCIA_MINIMA_CERCANIA_TRASCENDENTE = 1.3;
	// Disparar a gol
	int DISPARAR_DISTANCIA_MINIMA = 20;
	int DISPARAR_DISTANCIA_MEDIA = 32;
	int DISPARAR_DISTANCIA_LARGA = 54;
	// Angulo vertical
	int DISTANCIA_PARA_45_GRADOS = 48;
	int DISTANCIA_PARA_0_GRADOS = 12;
	// Desmarcarse
	int DESMARCANDOSE_ITERACIONES_MAXIMO = 30;
	int RADIO_DE_DESPLAZAMIENTO_MAXIMO = 25;// 25;
	int JUGADORES_A_DESMARCARSE = 6;
	int MINIMA_CERCANIA_A_JUGADOR = 15;
	// Hacer formacion
	int DISTANCIA_BALON_ATAQUE = 13;
	int DISTANCIA_BALON_DEFENSA = -12;

	/**
	 * Modifica el valor de las constantes. Es usada solo en tareas de
	 * depuracion.
	 */
	private void ModificarConstantes() {
		if (posec != posecionBalon) {
			posec = posecionBalon;
			if (posecionBalon) {
				// Desmarcarse
				DESMARCANDOSE_ITERACIONES_MAXIMO += 10;// = 40;
				RADIO_DE_DESPLAZAMIENTO_MAXIMO += 10;// = 35;
				JUGADORES_A_DESMARCARSE += 1;// = 7;
				MINIMA_CERCANIA_A_JUGADOR -= 3;// = 12;
				// Cubrir pase
				DISTANCIA_MAXIMA_BUSQUEDA_RIVAL -= 10;// = 20;
				DISTANCIA_MARCAJE_RIVAL += 4; // = 9;
				JUGADORES_MARCANDO -=2;// = 2;
				DISTANCIA_CORRECCION_FORMACION_MARCAJE -= 5;// = 10;
				// Hacer formacion
				DISTANCIA_BALON_ATAQUE -= 5;// = 8;
				DISTANCIA_BALON_DEFENSA -= 6; // = -18;
			}
			else {
				// Desmarcarse
				DESMARCANDOSE_ITERACIONES_MAXIMO -= 10;// = 40;
				RADIO_DE_DESPLAZAMIENTO_MAXIMO -= 10;// = 35;
				JUGADORES_A_DESMARCARSE -= 1;// = 7;
				MINIMA_CERCANIA_A_JUGADOR += 3;// = 12;
				// Cubrir pase
				DISTANCIA_MAXIMA_BUSQUEDA_RIVAL += 10;// = 20;
				DISTANCIA_MARCAJE_RIVAL -= 4; // = 9;
				JUGADORES_MARCANDO +=2;// = 2;
				DISTANCIA_CORRECCION_FORMACION_MARCAJE += 5;// = 10;
				// Hacer formacion
				DISTANCIA_BALON_ATAQUE += 5;// = 8;
				DISTANCIA_BALON_DEFENSA += 6; // = -18;
			}
		}
	}
	
	private void Desesperar(SituacionPartido sp) {
		int deses = (int)(sp.iteracion() / (Constantes.ITERACIONES / 4));
		if (deses > desespero && sp.golesRival() > sp.golesPropios()) {
			desespero = deses;
			// Pase exitoso
			MIN_DISTANCIA_PASE += 1 * desespero; // = 10; //+3
			probDistanciaPase -= 0.01 * desespero; // 0.13;
			probPresenciaRival -= 0.02 * desespero; // = 0.16;
			probHaciaAdelante += 0.02 * desespero; // = 0.21;
			probCercaniaAlArcoRival += 0.03 * desespero; // = 0.26;
			probLlegarPrimero -= 0.02 * desespero; // = 0.24;
			// Desmarcarse
			RADIO_DE_DESPLAZAMIENTO_MAXIMO += 1 * desespero; // = 40; //+
			MINIMA_CERCANIA_A_JUGADOR -= 1 * desespero; // = 12; //-
			// Cubrir pase
			DISTANCIA_MAXIMA_BUSQUEDA_RIVAL += 1 * desespero; // = 20; //-
			DISTANCIA_MARCAJE_RIVAL -= 0.5 * desespero; // = 9; //+
			JUGADORES_MARCANDO += 0.4 * desespero; // = 2; //-
			DISTANCIA_CORRECCION_FORMACION_MARCAJE += 2 * desespero; // = 10; //-
			// Hacer formacion
			DISTANCIA_BALON_ATAQUE -= 3 * desespero; // = 8; //-
			DISTANCIA_BALON_DEFENSA -= 4 * desespero; // = -8; //-
		}
	}

	public List<Comando> ejecutar(SituacionPartido sp) {
		comandos.clear();
		
		// Modifica variables
		ModificarConstantes();
		Desesperar(sp);
		
		CalcularVelocidadBalon(sp);
		EncontrarPorteroRival(sp);
		
		// QUIEN TIENE EL BALON?
		PosecionBalon(sp);
		
		// UBICACION BASICA SEGUN LA FORMACION
		HacerFormacion(sp);
		
		// PORTERO
		MoverPortero(sp);

		// ATACANDO
		// NO TENGO BALON
		// Desmarcarse
		Desmarcarse(sp);

		// DEFENDIENDO
		// Cubrir pase
		CubrirPase(sp);
		// Recuperar balon
		RecuperarBalon2(sp);

		// TENGO EL BALON
		dispararAGol = DispararAGol(sp);
		DominarAntesDeRematar(sp);
		darPase = DarPase(sp);
		acarrearBalon = AcarrearBalon(sp);

		// ACERCARSE AL BALON
		MoverJugadorCercano(sp);

		// ELIMINAR ESTO
		if (sp.puedenRematar().length > 0) {
			int a = 7;
			a += darPase;
		}
		if (sp.puedenRematarRival().length > 0) {
			int a = 8;
			a += darPase;
		}

		return comandos;
	}

	private void EncontrarPorteroRival(SituacionPartido sp) {
		if (indPorteroRival == -1) {
			indPorteroRival = 0;
			JugadorDetalle[] detallesRivales = sp.detalleJugadoresRivales();
			for (int j = 0; j < detallesRivales.length; j++) {
				if (detallesRivales[j].esPortero()) {
					indPorteroRival = j;
					break;
				}
			}
			porteroRival = sp.rivales()[indPorteroRival];
		}
	}

	private void DominarAntesDeRematar(SituacionPartido sp) {
		Posicion[] jugadores = sp.misJugadores();
		for (int i : sp.puedenRematar()) {
			if (JugadorDentroAreaGrandeRival(jugadores[i])) {
				int[] recuperadoresRival = getRecuperacionBalonRival(sp);
				if (recuperadoresRival.length <= 1
						|| (recuperadoresRival.length > 1
						&& recuperadoresRival[0] > 19)) {
					double potencia = PotenciaPase(3, sp.getRemateJugador(i), false, true);
					comandos.add(new ComandoGolpearBalon(i, Constantes.centroArcoSup, potencia, 0.0));
				}
			}
		}
	}
	
	private boolean JugadorDentroAreaGrandeRival(Posicion posicion) {
		if (Math.abs(posicion.getX()) <= (Constantes.LARGO_AREA_GRANDE / 2)) {
			if ((Constantes.LARGO_CAMPO_JUEGO / 2) - posicion.getY() <= Constantes.ANCHO_AREA_GRANDE) {
				return true;
			}
		}
		return false;
	}

	private void CalcularVelocidadBalon(SituacionPartido sp) {
		int iteraciones = 5;
		double[] posRecuperacion = sp.getTrayectoria(iteraciones);
		Posicion destino = new Posicion(posRecuperacion[0], posRecuperacion[1]);
		velocidadBalon = sp.balon().distancia(destino) / iteraciones;
	}

	private void MoverJugadorCercano(SituacionPartido sp) {
//		double MINIMA_DISTANCIA_CERCANIA_JUGADORES = 5;
//		Posicion[] jugadores = sp.misJugadores();
//		int i = sp.balon().indiceMasCercano(jugadores);
//		int j = jugadores[i].indiceMasCercano(jugadores, i);
//		if (i != 0 && jugadores[i].distancia(jugadores[j]) > 
//			MINIMA_DISTANCIA_CERCANIA_JUGADORES
//		) {
//			comandos.add(new ComandoIrA(i, sp
//				.balon()));
//		}
	}

	private void MoverPortero(SituacionPartido sp) {
		double x = 0;
		double y = 0;
		double a = 12;//15;
		double b = Constantes.centroArcoInf.distancia(formacionActual[0]) + 10; // Constantes.centroArcoInf.distancia(formacionActual[0]);
		double xj = sp.balon().getX();
		double yj = Constantes.centroArcoInf.distancia(new Posicion(0, sp.balon().getY()));
		double cj = 7;//10;
		
		double r = 1 / (a * a) + Math.pow(yj / (xj * b), 2);
		double s = 2 * (yj * cj) / (xj * b * b);
		double t = Math.pow(cj / b, 2) - 1;
		
		double det = s * s - 4 * r * t;
		if (det >= 0) {
			if (xj > 0)
				x = -s / (2 * r) + Math.sqrt(det) / (2 * r);
			else x = -s / (2 * r) - Math.sqrt(det) / (2 * r);
			y = Math.abs((yj * x) / xj);
			comandos.add(new ComandoIrA(0, Constantes.centroArcoInf.moverPosicion(x, y)));
		}
		if (!posecionBalon) {
			double[] posBalon = sp.getTrayectoria(10);
			Posicion destinoBalon = new Posicion(posBalon[0], posBalon[1]);
			double anguloPosteIzq = sp.balon().angulo(Constantes.posteIzqArcoInf);
			double anguloPosteDer = sp.balon().angulo(Constantes.posteDerArcoInf);
			double anguloDestino = sp.balon().angulo(destinoBalon);
			// hacia la porteria
			if (anguloDestino >= anguloPosteIzq
					&& anguloDestino <= anguloPosteDer) {
				if (sp.balon().getX() > 0)
					comandos.add(new ComandoIrA(0, Posicion.Interseccion(sp.misJugadores()[0], Constantes.posteDerArcoInf, sp.balon(), destinoBalon)));
				else comandos.add(new ComandoIrA(0, Posicion.Interseccion(sp.misJugadores()[0], Constantes.posteIzqArcoInf, sp.balon(), destinoBalon))); 
			}
		}
		//TODO mover portero
	}

	/**
	 * Encuentra quien tiene el balon en el momento asi el balon no este en
	 * dominio de algun jugador. No modifica comandos
	 * 
	 * @param sp
	 */
	private void PosecionBalon(SituacionPartido sp) {
		if (!sp.balon().isDentroCampoJuego(-1)) {
			if (jugando) {
				posecionBalon = !posecionBalon;
			}
			jugando = false;
		} else {
			if (sp.sacaRival())
				posecionBalon = false;
			else if (sp.saco())
				posecionBalon = true;
			else
				jugando = true;
		}
		if (sp.puedenRematarRival().length > 0) {
			posecionBalon = false;
		}
		if (sp.puedenRematar().length > 0) {
			if (!posecionBalon)
				LimpiarDesmarcandoseIteraciones();
			posecionBalon = true;
		}
		if (!posecionBalon) {
			jugadorOrigen = -1;
			jugadorDestino = -1;
		}
	}
	
	public int[] getRecuperacionBalonRival(SituacionPartido sp) {
        int it = 0;
        boolean found = false;
        Posicion pJug;
        double dist0, dist;
        int idxFound = -1;
        LinkedList<Double> founds = new LinkedList<Double>();
        JugadorDetalle detalles[] = sp.detalleJugadoresRivales();
        while (!found) {
            double[] posBalon = sp.getTrayectoria(it);
            if (!(new Posicion(posBalon[0], posBalon[1])).isDentroCampoJuego(2)) {
                return new int[]{};
            }
            if (posBalon[2] <= Constantes.ALTO_ARCO) {
                for (int i = 0; i < sp.rivales().length; i++) {
                    if (posBalon[2] <= (detalles[i].esPortero() ? Constantes.ALTO_ARCO : Constantes.ALTURA_CONTROL_BALON)) {
                        pJug = sp.rivales()[i];
                        dist0 = (double) it * Constantes.getVelocidad(sp.detalleJugadoresRivales()[i].getVelocidad());
                        dist = pJug.distancia(new Posicion(posBalon[0], posBalon[1]));
                        if (dist0 >= dist) {
                            found = true;
                            founds.add(dist);
                            founds.add((double) i);
                            idxFound = it;
                        }
                    }
                }
            }
            it++;
        }
        for (int i = 2; i < founds.size(); i = i + 2) {
            for (int j = 0; j < i; j = j + 2) {
                if (founds.get(i) < founds.get(j)) {
                    dist0 = founds.get(i);
                    dist = founds.get(i + 1);
                    founds.set(i, founds.get(j));
                    founds.set(i + 1, founds.get(j + 1));
                    founds.set(j, dist0);
                    founds.set(j + 1, dist);
                }
            }
        }
        for (int i = founds.size() - 1; i >= 0; i = i - 2) {
            founds.remove(i - 1);
        }
        founds.add(0, (double) idxFound);
        int[] result = new int[founds.size()];
        for (int i = 0; i < founds.size(); i++) {
            result[i] = (int) founds.get(i).doubleValue();
        }
        return result;
    }

	/**
	 * Le dice al jugador que puede recuperar el balon acercar a este. Se eligen
	 * jugadores segun la funcion sp.getTrayectoria. Modifica comandos.
	 * 
	 * @param sp
	 */
	private void RecuperarBalon2(SituacionPartido sp) {
		int[] recuperadores = sp.getRecuperacionBalon();
		int[] recuperadoresRival = getRecuperacionBalonRival(sp);
		Posicion[] jugadores = sp.misJugadores();
		
		if (recuperadores.length > 1) {
			double[] posRecuperacion = sp.getTrayectoria(recuperadores[0]);
			Posicion destino = new Posicion(posRecuperacion[0], posRecuperacion[1]);
			for (int i = 1; i < recuperadores.length; i++) {
				int j = recuperadores[i];
				if (j != 0) {
					//no portero
					comandos.add(new ComandoIrA(j, destino));
					break;
				}
				else {
					if (recuperadoresRival.length <= 1 || 
							(recuperadoresRival.length > 1 && recuperadoresRival[0] - 4 > recuperadores[0])) {
						//portero, retroceder
						if (recuperadores[i] == 0) {
							if (jugadores[0].distancia(sp.rivales()[jugadores[0].indiceMasCercano(sp.rivales())]) > 4) {
								if (sp.balon().distancia(jugadores[0]) - 4 < Constantes.DISTANCIA_CONTROL_BALON_PORTERO) {
									double altura = sp.alturaBalon();
									if (altura > Constantes.ALTO_ARCO - 1 ||
											velocidadBalon > 1.7) {
										Posicion des = jugadores[0].moverAngulo((jugadores[0].angulo(sp.balon()) - Math.PI), 3);
										if (des.isDentroCampoJuego(0))
											destino = des;
									}
								}
							}
						}
						comandos.add(new ComandoIrA(j, destino));
						break;
					}
				}
			}
		}
	}

	/**
	 * Marcaje a los posibles destinos de pase cuando no se tiene el balon.
	 * Modifica comandos.
	 * 
	 * @param sp
	 */
	private void CubrirPase(SituacionPartido sp) {
		boolean[] jugadorMarcando = { false, false, false, false, false, false,
				false, false, false, false, false };
		int jugador;
		double distanciaAlRival;
		Posicion[] jugadores = sp.misJugadores();
		Posicion[] rivales = sp.rivales();
		Posicion destino;

		int[] indicesRivales = jugadores[0].indicesMasCercanos(rivales);
		for (int j = 0; j < indicesRivales.length; j++) {// : indicesRivales) {
			if (j > JUGADORES_MARCANDO)
				break;
			int k = indicesRivales[j];
			jugador = rivales[k].indiceMasCercano(jugadores, 0);
			if (jugador != 0 && !jugadorMarcando[jugador]) {
				distanciaAlRival = formacionActual[jugador]
						.distancia(rivales[k]);
				if (distanciaAlRival < DISTANCIA_MAXIMA_BUSQUEDA_RIVAL) {
					destino = rivales[k].moverAngulo(rivales[k].angulo(sp
							.balon()), DISTANCIA_MARCAJE_RIVAL);
					jugadorMarcando[jugador] = true;
					if ((jugador == 1 
							|| jugador == 2
							|| jugador == 3) 
							&& distanciaAlRival > DISTANCIA_CORRECCION_FORMACION_MARCAJE
							&& destino.getY() < formacionActual[jugador].getY()) {
						formacionActual[jugador] = formacionActual[jugador].moverAngulo(formacionActual[jugador].angulo(destino), 0.1);
					}
					comandos.add(new ComandoIrA(jugador, destino));
				}
			}
		}
	}

	/**
	 * Busca, califica y selecciona la mejor opcion de pase. Modifica comandos.
	 * 
	 * @param sp
	 * @return double para el mejor pase exitoso.
	 */
	private double DarPase(SituacionPartido sp) {
		// valor entre 0 y 100. Se consultara a cada jugador su probabilidad de
		// recibir pase exitoso
		// si el valor es menor que la constante DARPASE
		double paseExitoso = 0;
		double actualPaseExitoso = 0;
		double potencia;
		double anguloVertical;
		double distanciaPase = 100;
		double actualDistanciaPase;
		Posicion[] jugadores = sp.misJugadores();
		Posicion actualDestino;
		Posicion destino = jugadores[10];

		for (int i : sp.puedenRematar()) {
			for (int j = 1; j < jugadores.length; j++) {
				if (i != j && 
						jugadores[i].distancia(jugadores[j]) > MIN_DISTANCIA_PASE) {
					// posicion modificada respecto al espacio a recorrer
					actualDestino = PosicionBalonAlEspacio(sp, i, jugadores[i],
							j, jugadores[j], sp.rivales(), 0.9, 50, false);
					// exito del pase elegido
					actualPaseExitoso = PaseExitoso(sp, jugadores[i], actualDestino);
					// distancia al jugador destino
					actualDistanciaPase = jugadores[i].distancia(actualDestino);
					// escoge el mejor
					if (actualPaseExitoso >= paseExitoso) {
						distanciaPase = actualDistanciaPase;
						paseExitoso = actualPaseExitoso;
						destino = actualDestino;
						jugadorDestino = j;
						jugadorOrigen = i;
						anguloEspacio = 180 * jugadores[j].angulo(destino) / Math.PI;
						distanciaEspacio = jugadores[j].distancia(destino);
						posicionDestino = destino;
					}
				}
			}
			potencia = PotenciaPase(distanciaPase, sp.detalleMisJugadores()[i]
					.getRemate(), sp.saco(), false);
			anguloVertical = AnguloVerticalPase(distanciaPase, sp.detalleMisJugadores()[i].getRemate());
			// Negocia con entre dar pase, o disparar
			if (paseExitoso > DARPASE)
				if (dispararAGol < DISPARO_MORTAL
						|| (dispararAGol >= DISPARO_MORTAL && paseExitoso >= PASE_MORTAL))
					comandos.add(new ComandoGolpearBalon(i, destino, potencia,
							anguloVertical));
		}
		return paseExitoso;
	}

	/**
	 * Encuentra una posicion cercana al destino cuya presencia de rivales sea
	 * lo menor posible.
	 * 
	 * @param origen
	 * @param destino
	 * @param rivales
	 * @param radioAvanceEspacio
	 * @return Posicion cercana o igual al destino.
	 */
	private Posicion PosicionBalonAlEspacio(SituacionPartido sp, int i, Posicion origen, int j, Posicion destino,
			Posicion[] rivales, double radioAvanceEspacio, double maximoRadioAvance, boolean minimaCercania) {
		Posicion destinoEspacio = destino;
		Posicion actualDestino;
		double angulo = - Math.PI / 8;
		double paseExitoso = 0;
		double actualPaseExitoso;
		int destinos = 11;
		Posicion[] jugadores = sp.misJugadores();
		int actualJugadorCercano = -1;
		double actualCercania = 0;
		
		for (int d = 0; d < destinos; d++) {
			for (int e = 0; e < 10; e++) {
				actualDestino = destino.moverAngulo(angulo, e
						* radioAvanceEspacio);
				actualJugadorCercano = actualDestino.indicesMasCercanos(jugadores, j)[0];
				actualCercania = actualDestino.distancia(jugadores[actualJugadorCercano]); 
				if (actualDestino.isDentroCampoJuego(-4)
						&& e * radioAvanceEspacio < maximoRadioAvance) {
					if (!minimaCercania || (minimaCercania && actualCercania > MINIMA_CERCANIA_A_JUGADOR)) {
						actualPaseExitoso = PaseExitoso(sp, origen, actualDestino);
						if (actualPaseExitoso > paseExitoso) {
							paseExitoso = actualPaseExitoso;
							destinoEspacio = actualDestino;
						}
					}
				}
			}
			angulo += (5 * Math.PI / 4) / destinos;
		}
		return destinoEspacio;
	}

	/**
	 * Decide cual sera la altura del pase. El resultado debe estar entre 0 y
	 * 45.
	 * 
	 * @param distanciaPase
	 * @return double angulo vertical
	 */
	private double AnguloVerticalPase(double distanciaPase, double remate) {
		if (distanciaPase > MAX_DISTANCIA_PASE_AEREO)
			return 24;
		if (distanciaPase > MAX_DISTANCIA_PASE_RASTRERO)
			return distanciaPase * 0.85;
		return 0;
	}

	/**
	 * Decide cual sera la potencia del pase dependiendo de la distancia del
	 * pase, la potencia del rematador, si es pelota quieta o si es para un
	 * acarrereo.
	 * 
	 * @param distancia
	 * @param remate
	 * @param sacando
	 * @param acarreo
	 * @return double entre 0 y 1
	 */
	private double PotenciaPase(double distancia, double remate,
			boolean sacando, boolean acarreo) {
		double potencia = 1.0;
		if (distancia < 48) {
			double medioCampo = Constantes.LARGO_CAMPO_JUEGO / 2;
			potencia = Math.sqrt(distancia / (medioCampo * remate));
		}
		//TODO potencia pase
		if (sacando)
			if (!acarreo)
				potencia = potencia * 1.80; // sacando
			else 
				potencia = potencia * 1.10; // acarreo sacando
		else if (acarreo)
			potencia = potencia * 0.80; // acarreo
		else if (distancia < MAX_DISTANCIA_PASE_RASTRERO)
			potencia = potencia * 1.32; // pase rastrero
		else
			potencia = potencia * 0.99; // pase aereo
		return potencia > 1.0 ? 1.0 : potencia;
	}

	/**
	 * Califica el exito que tendria un pase desde origen a destino. Se calcula
	 * segun la distancia del pase, la presencia del rival, la verticalizacion,
	 * la cercania al arco rival y si es posible llegar antes que el rival.
	 * 
	 * @param sp
	 * @param origen
	 * @param destino
	 * @return double de 0 a 100
	 */
	private double PaseExitoso(SituacionPartido sp, Posicion origen,
			Posicion destino) {
		// un pase exitoso esta en funcion del la posicion del jugador origen y
		// el jugador destino.
		// en base a la distancia entre ellos, entre menor distancia mayor
		// probabilidad.
		// depente tambien de la menor distancia a sus rivales
		// si el rival esta a una distancia menor a la distancia entre los del
		// pase, la proba deberia ser 0
		double paseExitoso = 1;
		double valorDistanciaPase = 0;
		//probDistanciaPase = 0.13;
		double valorPresenciaRival = 0;
		//probPresenciaRival = 0.16;
		double valorHaciaAdelante = 0;
		//probHaciaAdelante = 0.21;
		double valorCercaniaAlArcoRival = 0;
		//probCercaniaAlArcoRival = 0.26;
		double valorLlegarPrimero;
		//probLlegarPrimero = 0.24;
		double distanciaPase = origen.distancia(destino);

		if (sp.saco()) {
			//probDistanciaPase += 0.09;
			//probPresenciaRival -= 0.00;
			//probHaciaAdelante -= 0.08;
			//probCercaniaAlArcoRival -= 0.01;
			//probLlegarPrimero += 0.0;
		}
		if (distanciaPase < MIN_DISTANCIA_PASE)
			paseExitoso = 40;
		else {
			valorDistanciaPase = 100 - distanciaPase;
			valorPresenciaRival = 100 - PresenciaRivales(origen, destino, sp
					.rivales());
			valorHaciaAdelante = HaciaAdelante(origen, destino);
			valorCercaniaAlArcoRival = CercaniaAlArcoRival(destino);
			valorLlegarPrimero = LlegarPrimero(sp, destino);
			paseExitoso = ((valorDistanciaPase * probDistanciaPase)
					+ (valorPresenciaRival * probPresenciaRival)
					+ (valorHaciaAdelante * probHaciaAdelante)
					+ (valorCercaniaAlArcoRival * probCercaniaAlArcoRival) + (valorLlegarPrimero * probLlegarPrimero));
		}
		return paseExitoso;
	}

	/**
	 * Da un valor a la disputa entre dos jugadores rivales entre si para llegar
	 * a un punto fijo destino Depende principalmente de la distancia y la
	 * velocidad de cada jugador.
	 * 
	 * @param sp
	 * @param destino
	 * @return double entre 0 y 100
	 */
	private double LlegarPrimero(SituacionPartido sp, Posicion destino) {
		// valor entre 0 .. 100
		// depende de la velocidad de los mas cercanos al punto destino
		double llegarPrimero = 62;
		Posicion[] rivales = sp.rivales();
		Posicion[] jugadores = sp.misJugadores();
		int j = destino.indiceMasCercano(rivales);
		int i = destino.indiceMasCercano(jugadores);
		Posicion rival = rivales[j];
		Posicion jugador = jugadores[i];
		double distanciaJugador = destino.distancia(jugador);
		double distanciaRival = destino.distancia(rival);
		double velocidadJugador = Constantes.getVelocidad(sp.getVelocidadJugador(i));
		double velocidadRival = Constantes.getVelocidad(sp.getVelocidadJugadorRival(j));
		double trayectoJugador = distanciaJugador * (2 - velocidadJugador);
		double trayectoRival = distanciaRival * (2 - velocidadRival);
		double diferencia = trayectoRival - trayectoJugador;

		llegarPrimero = (diferencia + 28) * 100 / 60;
		return llegarPrimero;
	}

	/**
	 * Cercania al arco rival en un valor entre 40 y 80.
	 * 
	 * @param posicion
	 * @return double de 40 a 80.
	 */
	private double CercaniaAlArcoRival(Posicion posicion) {
		// si esta mas cerca al arco rival tendra mejor valor
		// empieza a contar desde -52 a 52 tomando valores de 40 .. 80
		double cercaniaAlArcoRival = 1;

		cercaniaAlArcoRival = Constantes.centroArcoSup.distancia(posicion) * 50 / Constantes.LARGO_CAMPO_JUEGO;
		cercaniaAlArcoRival = 90 - cercaniaAlArcoRival;
		return cercaniaAlArcoRival;
	}

	/**
	 * Valora la verticalizacion entre dos posiciones
	 * 
	 * @param origen
	 * @param destino
	 * @return
	 */
	private double HaciaAdelante(Posicion origen, Posicion destino) {
		double haciaAdelante;
		double diferenciaY = Math.abs(origen.getY() - destino.getY());

		if (origen.getY() - AVANCE_MEDIA_PASE_RETRASADO < destino.getY()) {
			// para adelante
			if (diferenciaY > AVANCE_MAXIMA_PASE_ADELANTE)
				haciaAdelante = 50;
			else
				haciaAdelante = 65 + (diferenciaY * 15 / AVANCE_MAXIMA_PASE_ADELANTE);
		} else {
			// para atras
			if (diferenciaY > AVANCE_MAXIMA_PASE_RETRASADO)
				haciaAdelante = 30;
			else
				haciaAdelante = 65 - (diferenciaY * 10 / AVANCE_MAXIMA_PASE_RETRASADO);
		}
		return haciaAdelante;
	}

	/**
	 * Encuentra la mayor presencia de rival entre dos posiciones amigas. La
	 * mayor valoracion (100) corresponde a una presencia muy alta de rivales en
	 * el camino.
	 * 
	 * @param jugadorOrigen
	 * @param jugadorDestino
	 * @param rivales
	 * @return double valorando entre 0 y 100
	 */
	private double PresenciaRivales(Posicion jugadorOrigen,
			Posicion jugadorDestino, Posicion[] rivales) {
		double[] presenciasRivales = new double[rivales.length];
		double presenciaRival;
		int[] r = jugadorDestino.indicesMasCercanos(rivales);
		
		for (int i = 0; i < 4; i++) {// rivales.length; i++) {
			presenciasRivales[i] = PresenciaRival(jugadorOrigen,
					jugadorDestino, rivales[r[i]]);
		}
		presenciaRival = EscogerMayorDeLista(presenciasRivales);
		return presenciaRival;
	}

	/**
	 * Valora la presencia de un rival entre dos posiciones amigas. La mayor
	 * valoracion indica la presencia alta de el rival entre ellos.
	 * 
	 * @param jugadorOrigen
	 * @param jugadorDestino
	 * @param rival
	 * @return double entre 0 y 100.
	 */
	private double PresenciaRival(Posicion jugadorOrigen,
			Posicion jugadorDestino, Posicion rival) {
		// la presencia de un rival esta en su cercania al jugador
		// entre menor distancia mayor presencia
		// si el rival se interpone entre el origen y el destino su presencia
		// sera mayor
		// se presenta cuando la distancia al origen es menor desde el rival que
		// desde el destino
		// y la distancia entre los angulos a) del origen y al rival y b) del
		// origen al destino es peque�o
		// retorna un valor entre 0 y 100: 0 es lo mejor
		double presenciaRival = 101;
		double valorCercaniaADestino; // al destino
		double probCercaniaADestino = 0.50; // 0.65;
		double valorAngulo;
		double probAngulo = 0.35; // 0.35;
		double valorCercaniaAOrigen;
		double probCercaniaAOrigen = 0.15;
		double distanciaPase = jugadorOrigen.distancia(jugadorDestino);
		double distanciaRivalDestino = jugadorDestino.distancia(rival);
		double distanciaRivalOrigen = jugadorOrigen.distancia(rival);
		double anguloDestino = jugadorOrigen.angulo(jugadorDestino);
		anguloDestino = anguloDestino > 0 ? anguloDestino * 180 / Math.PI
				: 360 + (anguloDestino * 180 / Math.PI);
		double anguloRival = jugadorOrigen.angulo(rival);
		anguloRival = anguloRival > 0 ? anguloRival * 180 / Math.PI
				: 360 + (anguloRival * 180 / Math.PI);
		double diferenciaAngulos = Math.abs(anguloDestino - anguloRival);
		if (diferenciaAngulos > 180)
			diferenciaAngulos = 360 - diferenciaAngulos;
		double DISTANCIA_PRESENCIA_TRASCENDENTE = 7 + distanciaPase / 0.9;//1.3;
		double INICIO_CERCANIA_TRASCENDENTE = 7 + distanciaPase / 0.7;//1.1;

		valorCercaniaADestino = CercaniaADestino(distanciaPase,
				distanciaRivalOrigen, distanciaRivalDestino,
				DISTANCIA_PRESENCIA_TRASCENDENTE, INICIO_CERCANIA_TRASCENDENTE);
		valorAngulo = AnguloPresenciaRival(distanciaPase,
				distanciaRivalDestino, diferenciaAngulos);
		valorCercaniaAOrigen = CercaniaAOrigen(distanciaRivalOrigen,
				DISTANCIA_PRESENCIA_TRASCENDENTE);

		presenciaRival = (valorCercaniaADestino * probCercaniaADestino)
				+ (valorAngulo * probAngulo)
				+ (valorCercaniaAOrigen * probCercaniaAOrigen);
		return presenciaRival;
	}

	/**
	 * Cercania a origen que depende de la distancia del rival al origen y esta
	 * limitada por un radio maximo.
	 * 
	 * @param distanciaRivalOrigen
	 * @param distanciaPresenciaTrascendente
	 * @return double entre 0 y 100.
	 */
	private double CercaniaAOrigen(double distanciaRivalOrigen,
			double distanciaPresenciaTrascendente) {
		double valorCercaniaAOrigen = 40;
		if (distanciaRivalOrigen < distanciaPresenciaTrascendente) {
			valorCercaniaAOrigen = 75 - (distanciaRivalOrigen * 30 / distanciaPresenciaTrascendente);
		}
		return valorCercaniaAOrigen;
	}

	/**
	 * Angulo de presencia del rival respecto al destino.
	 * 
	 * @param distanciaPase
	 * @param distanciaRivalDestino
	 * @param diferenciaAngulos
	 * @return double
	 */
	private double AnguloPresenciaRival(double distanciaPase,
			double distanciaRivalDestino, double diferenciaAngulos) {
		double valorAngulo;
		if (distanciaPase < distanciaRivalDestino) {
			valorAngulo = 20;
		} else {
			// falta incluir la distancia del rival al origen
			if (diferenciaAngulos < 35) {
				valorAngulo = 85 - (diferenciaAngulos * 25 / 35);
			} else if (diferenciaAngulos < 90) {
				valorAngulo = 60 - ((diferenciaAngulos - 35) * 45 / 55);
			} else {
				valorAngulo = 15;
			}
		}
		return valorAngulo;
	}

	/**
	 * Cercania al destino de los rivales que depende de la distancia al origen
	 * y limitantes minimo y maximo.
	 * 
	 * @param distanciaPase
	 * @param distanciaRivalOrigen
	 * @param distanciaRivalDestino
	 * @param distanciaPresenciaTrascendente
	 * @param inicioCercaniaTrascendente
	 * @return
	 */
	private double CercaniaADestino(double distanciaPase,
			double distanciaRivalOrigen, double distanciaRivalDestino,
			double distanciaPresenciaTrascendente,
			double inicioCercaniaTrascendente) {
		double valorCercaniaADestino;
		if (distanciaPase > distanciaRivalOrigen) {
			if (distanciaRivalDestino > distanciaPresenciaTrascendente) {
				if (distanciaRivalDestino > 50)
					valorCercaniaADestino = 30 - (distanciaRivalDestino / 10);
				else
					valorCercaniaADestino = (65 - distanciaRivalDestino);
			} else if (distanciaRivalOrigen < DISTANCIA_MINIMA_CERCANIA_TRASCENDENTE) {
				valorCercaniaADestino = 40 - (distanciaRivalDestino / 10);
			} else {
				valorCercaniaADestino = (100 - (distanciaRivalDestino * 60 / distanciaPresenciaTrascendente));
			}
		} else {
			if (distanciaRivalDestino > inicioCercaniaTrascendente) {
				valorCercaniaADestino = 40 - (distanciaRivalDestino / 10);
			} else {
				valorCercaniaADestino = 85 - (distanciaRivalDestino * 50 / inicioCercaniaTrascendente);
			}
		}
		return valorCercaniaADestino;
	}

	/**
	 * De una lista, escoge el menor
	 * 
	 * @param valores
	 * @return
	 */
	/*
	 * private double EscogerMenorDeLista(double[] valores) { double valor =
	 * 100; double actual;
	 * 
	 * for (int i = 0; i < valores.length; i++) { actual = valores[i]; if
	 * (actual < valor) { valor = actual; } } return valor; }
	 */

	/**
	 * De una lista escoge la mayor
	 * 
	 * @param valores
	 * @return
	 */
	private double EscogerMayorDeLista(double[] valores) {
		double valor = 0;
		double actual;

		for (int i = 0; i < valores.length; i++) {
			actual = valores[i];
			if (actual > valor) {
				valor = actual;
			}
		}
		return valor;
	}
	
	/*
	private double EscogerPromedioDeLista(double[] valores) {
		double valor = 0;
		int j = 0;

		for (int i = 0; i < valores.length; i++) {
			if (valores[i] != 0 && valores[i] != 100) {
				j++;
				valor += valores[i];
			}
		}
		valor = valor / j;
		return valor;
	}*/

	/**
	 * Disparar a gol. Modifica comandos.
	 * 
	 * @param sp
	 * @return double de 0 a 100.
	 */
	private double DispararAGol(SituacionPartido sp) {
		double dispararAGol = 100;
		Double distanciaAArco;
		double valorDistanciaAArco;
		double probDistanciaAArco = 0.48;
		double valorPresenciaRival;
		double probPresenciaRival = 0.18;
		double valorPorteroFueraDelArco;
		double probPorteroFueraDelArco = 0.34;
		Posicion objetivoEnArco;

		Posicion[] jugadores = sp.misJugadores();
		Posicion[] rivalesNoPortero = sp.rivales().clone();
		rivalesNoPortero[0] = rivalesNoPortero[10];

		for (int i : sp.puedenRematar()) {
			distanciaAArco = jugadores[i].distancia(Constantes.centroArcoSup);
			if (distanciaAArco < DISPARAR_DISTANCIA_MINIMA) {
				valorDistanciaAArco = 100;
			} else if (distanciaAArco < DISPARAR_DISTANCIA_MEDIA) {
				valorDistanciaAArco = 100 - ((distanciaAArco - DISPARAR_DISTANCIA_MINIMA) * 25 / (DISPARAR_DISTANCIA_MEDIA - DISPARAR_DISTANCIA_MINIMA));
			} else if (distanciaAArco < DISPARAR_DISTANCIA_LARGA) {
				valorDistanciaAArco = 75 - ((distanciaAArco - DISPARAR_DISTANCIA_MEDIA) * 25 / (DISPARAR_DISTANCIA_LARGA - DISPARAR_DISTANCIA_MEDIA));
			} else {
				valorDistanciaAArco = 50 - ((distanciaAArco - DISPARAR_DISTANCIA_LARGA) * 50 / (Constantes.LARGO_CAMPO_JUEGO - DISPARAR_DISTANCIA_LARGA));
			}
			valorPresenciaRival = 100 - PresenciaRivales(jugadores[i],
					Constantes.centroArcoSup, rivalesNoPortero);
			valorPorteroFueraDelArco = 55 + (30 * Constantes.centroArcoSup
					.distancia(porteroRival) / (1.4 * Constantes.DISTANCIA_PENAL));
			if (valorPorteroFueraDelArco > 90) valorPorteroFueraDelArco = 90;
			objetivoEnArco = ObjetivoEnArco(porteroRival, sp.balon(), sp.getErrorJugador(i), distanciaAArco);
			double anguloVertical = AnguloVertical(distanciaAArco, sp
					.detalleMisJugadores()[i].getRemate());
			dispararAGol = ((valorDistanciaAArco * probDistanciaAArco)
					+ (valorPresenciaRival * probPresenciaRival) + (valorPorteroFueraDelArco * probPorteroFueraDelArco));

			comandos.add(new ComandoGolpearBalon(i, objetivoEnArco, 1,
					anguloVertical));
		}
		return dispararAGol;
	}

	/**
	 * Angulo vertical para la altura del disparo
	 * 
	 * @param distanciaAArco
	 * @param remate
	 * @return double entre 8 y 35
	 */
	private double AnguloVertical(Double distanciaAArco, double remate) {
		//TODO angulo disparo
		if (distanciaAArco > 60)
			return 30;
		double anguloVertical = 2 * (Constantes.ALTO_ARCO + 1) / ((Constantes.LARGO_CAMPO_JUEGO - distanciaAArco) / 2);
		anguloVertical = Math.atan(anguloVertical);
		anguloVertical = anguloVertical + anguloVertical * (1 - Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE);
		anguloVertical = anguloVertical * 180 / Math.PI;
		return anguloVertical;
	}

	/**
	 * Objetivo en el arco. Encuentra la mejor colocacion del balon en el arco.
	 * 
	 * @param portero
	 * @param balon
	 * @return Posicion dentro del arco.
	 */
	private Posicion ObjetivoEnArco(Posicion portero, Posicion balon, double error, Double distanciaAArco) {
		Posicion objetivoEnArco;
		double anguloPortero = Constantes.centroArcoSup.angulo(portero);
		double anguloBalon = Constantes.centroArcoSup.angulo(balon);
		double diferenciaAngulos;

		anguloPortero = anguloPortero > 0 ? anguloPortero
				: 360 + (anguloPortero * 180 / Math.PI);
		anguloBalon = anguloBalon > 0 ? anguloBalon * 180 / Math.PI
				: 360 + (anguloBalon * 180 / Math.PI);
		diferenciaAngulos = anguloPortero - anguloBalon;
		if (diferenciaAngulos > 180)
			diferenciaAngulos = 360 - diferenciaAngulos;
		if (portero.distancia(Constantes.centroArcoSup) > 11)
			objetivoEnArco = Constantes.centroArcoSup;
		else {
			double dx = (1 - error) * 6; 
			if (diferenciaAngulos > 1.0 && diferenciaAngulos < 25)
				objetivoEnArco = Constantes.posteIzqArcoSup.moverPosicion(dx,
						0);
			else if (diferenciaAngulos < -1.0 && diferenciaAngulos > -25)
				objetivoEnArco = Constantes.posteDerArcoSup.moverPosicion(-dx,
						0);
			else
				objetivoEnArco = Constantes.centroArcoSup;
		}
		distanciaAArco = balon.distancia(objetivoEnArco);
		return objetivoEnArco;
	}

	/**
	 * Acarrear el balon. Le da un valor a la posibilidad de regate teniendo en
	 * cuenta la presencia de rivales.
	 * 
	 * @param sp
	 * @return double entre 0 a 100
	 */
	private double AcarrearBalon(SituacionPartido sp) {
		double acarrearBalon = 0;
		double valorPresenciaRival;
		double probPresenciaRival = 0.45;
		double valorPosicionY;
		double probPosicionY = 0.40;
		double valorPosicionX;
		double probPosicionX = 0.15;
		double potenciaAcarreo = 0.4;
		double avanceY = 9.5;
		Posicion[] jugadores = sp.misJugadores();
		Posicion balon = sp.balon();
		double anguloBalon = Constantes.centroArcoSup.angulo(balon);
		anguloBalon = anguloBalon > 0 ? anguloBalon * 180 / Math.PI
				: 360 + (anguloBalon * 180 / Math.PI);

		Posicion[] rivalesNoPortero = sp.rivales().clone();
		rivalesNoPortero[0] = rivalesNoPortero[1];
		for (int i : sp.puedenRematar()) {
			if (true){//i != 0 && !sp.saco()) {
				Posicion destino = PosicionDestinoAcarreo(sp, jugadores[i]);
				valorPresenciaRival = 100 - PresenciaRivales(jugadores[i],
						destino, rivalesNoPortero);
				valorPosicionY = 81 - (26 * (Constantes.centroArcoSup
						.distancia(jugadores[i])) / Constantes.LARGO_CAMPO_JUEGO);
				valorPosicionX = 50 + (30 * Math.abs(jugadores[i].getX()) / 
						(Constantes.ANCHO_CAMPO_JUEGO / 2));
				acarrearBalon = ((valorPresenciaRival * probPresenciaRival) + 
						(valorPosicionY * probPosicionY) +
						(valorPosicionX * probPosicionX));
				if (avanceY < 5) acarrearBalon = acarrearBalon * 0.81;
				if (acarrearBalon > MINIMO_ACARREAR_BALON) {
					if (darPase <= PASE_MORTAL) {
						potenciaAcarreo = PotenciaPase(avanceY, sp
								.detalleMisJugadores()[i].getRemate(), sp.saco(), true);
						if (dispararAGol <= DISPARO_MORTAL)
						{
							comandos.add(new ComandoGolpearBalon(i, destino,
									potenciaAcarreo, false));
							anguloEspacio = jugadores[i].angulo(destino);
							distanciaEspacio = jugadores[i].distancia(destino);
						}
						else if (acarrearBalon > ACARREO_MORTAL) {
							comandos.add(new ComandoGolpearBalon(i, destino,
									potenciaAcarreo, false));
							anguloEspacio = jugadores[i].angulo(destino);
							distanciaEspacio = jugadores[i].distancia(destino);
						}
					}
				}
			}
		}
		return acarrearBalon;
	}

	/**
	 * Encuentra una posicion o direccion para acarrear el balon.
	 * 
	 * @param sp
	 * @param origen
	 * @return Posicion de destino para el acarreo
	 */
	private Posicion PosicionDestinoAcarreo(SituacionPartido sp, Posicion origen) {
		double avance = 7.0;
		double angulo = - Math.PI / 8;
		int cantDirecciones = 32;
		double valorPresenciaRival = 101;
		double probPresenciaRival = 0.35;
		double valorCercaniaAlArcoRival;
		double probCercaniaAlArcoRival = 0.50;
		double valorLlegarPrimero;
		double probLlegarPrimero = 0.15;
		double posicionDestinoAcarreo = 0;
		double actualPosicionDestinoAcarreo;
		Posicion destino = origen;
		Posicion actualDestino;
		
		Posicion[] rivalesNoPortero = sp.rivales().clone();
		rivalesNoPortero[0] = rivalesNoPortero[1];
		if (origen.angulo(Constantes.centroArcoSup) > - Math.PI / 4 ||
				origen.angulo(Constantes.centroArcoSup) < - 3 * Math.PI / 4) {
			probPresenciaRival -= 0.04;
			probCercaniaAlArcoRival += 0.01;
			probLlegarPrimero -= 0.06;
		}
		for (int i = 0; i < cantDirecciones; i++) {
			angulo += (Math.PI / cantDirecciones) + (2 * Math.PI / 8) / cantDirecciones;
			actualDestino = origen.moverAngulo(angulo, avance);
			if (actualDestino.isDentroCampoJuego(-3)) {
				valorPresenciaRival = 90 - PresenciaRivales(origen, actualDestino, rivalesNoPortero);
				valorCercaniaAlArcoRival = CercaniaAlArcoRival(actualDestino);
				valorLlegarPrimero = LlegarPrimero(sp, actualDestino);
				actualPosicionDestinoAcarreo = ((valorPresenciaRival * probPresenciaRival) +
						(valorCercaniaAlArcoRival * probCercaniaAlArcoRival) +
						(valorLlegarPrimero * probLlegarPrimero));
				
				if (actualPosicionDestinoAcarreo > posicionDestinoAcarreo) {
					destino = actualDestino;
					posicionDestinoAcarreo = actualPosicionDestinoAcarreo;
				}
			}
		}
		return destino;
	}

	/**
	 * Se desmarcan todos los jugadores excepto los defensas y el portero
	 * Modifica comandos.
	 * 
	 * @param sp
	 */
	private void Desmarcarse(SituacionPartido sp) {
		int avance = 7;
		Posicion destino;
		Posicion[] jugadores = sp.misJugadores();
		int[] indicesJugadores = sp.balon().indicesMasCercanos(
				sp.misJugadores(), 0, 1, 2, 3);
		int j = 1;

		for (int i : indicesJugadores) {
			if (i == 10) {
				int a = 0;
				a++;
			}
			if (j > JUGADORES_A_DESMARCARSE)
				break;
			j++;
			double angulo = 180 * desmarcandoseDireccion[i] / Math.PI;
			destino = jugadores[i].moverAngulo(angulo, avance);
			double distanciaDesplazada = formacionActual[i]
					.distancia(jugadores[i]);
			int jugadorCercano = jugadores[i].indicesMasCercanos(jugadores)[1];
			double minimaCercania = jugadores[i]
					.distancia(jugadores[jugadorCercano]);
			if (i != jugadorDestino && i != 0 && i != 1 && i != 3) {
				if (desmarcandoseIteracion[i] > 0
						&& distanciaDesplazada < RADIO_DE_DESPLAZAMIENTO_MAXIMO
						&& minimaCercania > MINIMA_CERCANIA_A_JUGADOR
						&& destino.isDentroCampoJuego(-4)
						&& sp.balon().indiceMasCercano(jugadores) != i) {
					comandos.add(new ComandoIrA(i, destino));
					desmarcandoseIteracion[i] -= 1;
				} else {
					int iter = desmarcandoseIteracion[i];
					desmarcandoseIteracion[i] = DESMARCANDOSE_ITERACIONES_MAXIMO;
					if (jugadorDestino != -1)
						destino = PosicionBalonAlEspacio(sp, jugadorDestino,
								jugadores[jugadorDestino], i, jugadores[i], sp
										.rivales(), 1.4,
								RADIO_DE_DESPLAZAMIENTO_MAXIMO, true);
					else
						destino = PosicionBalonAlEspacio(sp, -1, sp.balon(), i,
								jugadores[i], sp.rivales(), 1.4,
								RADIO_DE_DESPLAZAMIENTO_MAXIMO, true);
					desmarcandoseDireccion[i] = jugadores[i].angulo(destino);
					if (iter == 0)
						iter = 0;
				}
			}
		}
	}

	/**
	 * Detiene los movimientos de desmarcaje colocando la cantidad de
	 * iteraciones faltantes a 0
	 */
	private void LimpiarDesmarcandoseIteraciones() {
		for (int i = 0; i < desmarcandoseIteracion.length; i++) {
			desmarcandoseIteracion[i] = 0;
		}
	}

	/**
	 * Indica a los jugadores tomar una posicion de formacion segun la posicion
	 * del balon y quien este atacando.
	 * 
	 * @param sp
	 */
	private void HacerFormacion(SituacionPartido sp) {
		Posicion[] jugadores = sp.misJugadores();
		formacionActual = getPosicionMedia();

		if (sp.balon().getY() > DISTANCIA_BALON_ATAQUE) {
			if (!sp.sacaRival() && posecionBalon)
				formacionActual = getPosicionAtaque();
		} else if (sp.balon().getY() < DISTANCIA_BALON_DEFENSA) {
			if (!sp.saco() && !posecionBalon)
				formacionActual = getPosicionDefensa();
		}
		for (int i = 0; i < jugadores.length; i++) {
			comandos.add(new ComandoIrA(i, formacionActual[i]));
		}
	}
}
